package freedom.needle.task.impl;

import java.util.Date;

import freedom.needle.dao.BizCustBaseInfoDao;
import freedom.needle.dao.TradeXmlTempDao;
import freedom.needle.entity.BizCustBaseInfo;
import freedom.needle.entity.TradeXmlTemp;
import freedom.needle.enums.TradeXmlStateEnum;
import freedom.needle.enums.TradeXmlStatusEnum;
import freedom.needle.queue.BlockingDistinctQueue;
import freedom.needle.tps.TpsWindow;
import freedom.needle.util.XmlUtil;

public class TradeXmlTempConsumer extends AbstractCycleTask {

	private BlockingDistinctQueue<TradeXmlTemp> queue;
	private TradeXmlTempDao tradeXmlTempDao;
	private BizCustBaseInfoDao bizCustBaseInfoDao;
	private TpsWindow tpsWindow;

	private static final String hql_1 = "update TradeXmlTemp set status=?1,updateTime=?2 where id=?3 and status=?4";
	private static final String hql_2 = "update TradeXmlTemp set status=?1,state=?2,updateTime=?3 where id=?4 and status=?5";
	private static final String hql_3 = "update TradeXmlTemp set retry=retry+1,updateTime=?1 where id=?2 and status=?3";
	private static final String hql_4 = "update TradeXmlTemp set retry=retry+1,status=?1,state=?2,updateTime=?3 where id=?4 and status=?5";

	@Override
	public Class<?> getClazz() {
		return TradeXmlTempConsumer.class;
	}

	@Override
	public void execute() throws Exception {
		TradeXmlTemp tradeXmlTemp = null;
		int count = -1;
		try {
			tradeXmlTemp = queue.take();

			// 尝试获得执行报文的机会（乐观锁）
			Object[] params = new Object[] { TradeXmlStatusEnum.STEP_1_DOING, new Date(), tradeXmlTemp.getId(), TradeXmlStatusEnum.STEP_1_READY };
			count = tradeXmlTempDao.update(hql_1, params);
			if (count == 1) {
				// 执行报文
				try {
					BizCustBaseInfo bizCustBaseInfo = XmlUtil.genObjByXml(BizCustBaseInfo.class, tradeXmlTemp.getXml());
					bizCustBaseInfoDao.save(bizCustBaseInfo);
					// 记录执行结果
					params = new Object[] { TradeXmlStatusEnum.STEP_1_DONE, TradeXmlStateEnum.COMPLETE_NORMALLY, new Date(), tradeXmlTemp.getId(), TradeXmlStatusEnum.STEP_1_DOING };
					tradeXmlTempDao.update(hql_2, params);
					this.sleepStrategy.decrease();// 执行成功，降低休眠时间，提高执行频率
					// 记录TPS
					if (tpsWindow.isOpen())
						tpsWindow.inc();
				} catch (Exception e) {
					// TODO 可以根据需要自定义并处理异常
					if(logger.isWarnEnabled()) {
						logger.warn("", e);
					}

					// 某些异常情况下(网络不稳定造成的通信偶然中断)，允许重试N次
					this.sleepStrategy.increase();// 提高休眠时间，降低执行频率
//					if (tradeXmlTemp.getRetry() < 3) {
//						params = new Object[] { new Date(), tradeXmlTemp.getId(), TradeXmlStatusEnum.STEP_1_DOING };
//						tradeXmlTempDao.update(hql_3, params);
//					} else {
//						params = new Object[] { TradeXmlStatusEnum.STEP_1_ERROR, TradeXmlStateEnum.RETRY_LIMITLY, new Date(), tradeXmlTemp.getId(), TradeXmlStatusEnum.STEP_1_DOING };
//						tradeXmlTempDao.update(hql_4, params);
//					}

					// TODO 其他已知异常的处理

					// 其他未知异常的处理
//					params = new Object[] { TradeXmlStatusEnum.STEP_1_ERROR, TradeXmlStateEnum.UNKNOWN_ERROR, new Date(), tradeXmlTemp.getId(), TradeXmlStatusEnum.STEP_1_DOING };
//					tradeXmlTempDao.update(hql_2, params);
				}
			}

			// TODO
			// 获得了执行报文的机会，却由于某些原因（如强制停止应用）导致任务死在执行中。需要独立的线程定期将这部分长期处于执行中的任务状态重置，使其重新执行

		} finally {
			queue.destroy(tradeXmlTemp);
		}
		// 观察乐观锁对TPS的损耗情况
		if (logger.isDebugEnabled() && count == 0) {
			logger.debug("not update.");
		}
		// 业务逻辑错误
		if (logger.isErrorEnabled() && count > 1) {
			logger.error("updated more than one!");
		}
	}

	/**
	 * @param queue
	 *            the queue to set
	 */
	public void setQueue(BlockingDistinctQueue<TradeXmlTemp> queue) {
		this.queue = queue;
	}

	/**
	 * @param tradeXmlTempDao
	 *            the tradeXmlTempDao to set
	 */
	public void setTradeXmlTempDao(TradeXmlTempDao tradeXmlTempDao) {
		this.tradeXmlTempDao = tradeXmlTempDao;
	}

	/**
	 * @param tpsWindow
	 *            the tpsWindow to set
	 */
	public void setTpsWindow(TpsWindow tpsWindow) {
		this.tpsWindow = tpsWindow;
	}

	/**
	 * @param bizCustBaseInfoDao the bizCustBaseInfoDao to set
	 */
	public void setBizCustBaseInfoDao(BizCustBaseInfoDao bizCustBaseInfoDao) {
		this.bizCustBaseInfoDao = bizCustBaseInfoDao;
	}

}
